//
// Created by song on 16-11-23.
//

#ifndef C0COMPILER_SIMPLEMIPSGENERATOR_H
#define C0COMPILER_SIMPLEMIPSGENERATOR_H


#include "IR.h"
#include "Optimizer.h"
#include "IRVisitor.h"
#include "MIPsIns.h"
#include "SimpleIRVisitor.h"

class SimpleMIPSGenerator{
//    stringstream mipsIns;
    MIPsIns& ins;
    vector<IR*> irList;
    set<string> stringSet;
    set<TmpVar*> globalVars;
    set<TmpVar*> localVarSet;
public:

    SimpleMIPSGenerator(IRList* irList, MIPsIns& ins):ins(ins){
        this->irList = irList->clist;
    }

    void transform() {
        ins.define("PRINT_INT", "1");
        ins.define("PRINT_STR", "4");
        ins.define("PRINT_CHAR","11");
        ins.define("READ_INT",  "5");
        ins.define("READ_CHAR", "12");
        ins.dataSegmentBegin();
        ins.globalString("_err","\"runtime error: \"");

        for(int i=0; i< TmpVar::varList.size(); i++){
            TmpVar* var = TmpVar::varList[i];
            if(var->type==TmpVar::STRING){
                ins.globalString(var->name(), var->content);
            }
        }


        if(Config::irSymbolTable) cout << "#########################################" << endl;
        bool global=true;
        int functionStart = 0;
        int offset = -1; //offset to caller's $sp, 1 means 1 word(4bytes). -1 now points to $ra
        TmpVar* functionLabel;
        vector<TmpVar*> localVarList;
        for(int i=0; i<irList.size(); i++) {
            IR *ir = irList[i];
            if (global) {
                switch (ir->op) {
                    case ASSIGN:
                        ins.globalVariable(ir->result->name(), ir->left->value);
                        globalVars.insert(ir->result); // global variable mark.
                        break;
                    case ARRAY:
                        ins.globalEmptyArray(ir->result->name(), ir->left->value * 4);
                        globalVars.insert(ir->result); // global variable mark.
                        break;
                    case JMP:
                        ins.textSegmentBegin();
                        ins.j(ir->result->name());
                        global = false;
                        functionStart = i + 1;
                        break;
                }
            } else {
                switch(ir->op){
                    case FUNBEGIN:
                        offset = -1;
                        functionLabel = ir->label;
                        break;
                    case FUNEND:
                        functionLabel->value = (-offset)*4;
                        for(int j=0;j<localVarList.size();j++){
                            TmpVar* tmpVar = localVarList[j];
                            tmpVar->value = ((-offset) + tmpVar->value) * 4;
                            if(Config::irSymbolTable) cout << tmpVar->debugStr() << endl;
                        }
                        if(Config::irSymbolTable) cout << "--------------------------------" << endl;
                        localVarList.clear();
                        localVarSet.clear();
                        break;
                    case PUSHARR:
                        offset -= ir->left->value;
                        ir->result->value = offset; // start address of array is lower than the end address
                        localVarList.push_back(ir->result);
                        break;
                    default:
                        if (notAlloc(ir->left)){
                            offset--;
                            ir->left->value = offset;
                            localVarList.push_back(ir->left);
                            localVarSet.insert(ir->left);
                        }
                        if (notAlloc(ir->right)){
                            offset--;
                            ir->right->value = offset;
                            localVarList.push_back(ir->right);
                            localVarSet.insert(ir->right);
                        }
                        if (notAlloc(ir->result)){
                            offset--;
                            ir->result->value = offset;
                            localVarList.push_back(ir->result);
                            localVarSet.insert(ir->result);
                        }
                }
            }
        }


        SimpleIRVisitor irVisitor(globalVars, ins);
        for(int i=functionStart; i<irList.size(); i++){
            IR* ir = irList[i];
            if(ir->label!=NULL){
                ins.label(ir->label->name());
            }
            irVisitor.visit(ir);
        }
        ins.li(A0, 0);
        ins.li(V0, 17);
        ins.syscall();
    }

    string label(IR* ir){
        stringstream r;
        if(ir->label==NULL){
            return "";
        }else{
            r << ir->label->name() << ":" << endl;
            return r.str();
        }
    }

    string blank(){
        return "\t";
    }

    bool notAlloc(TmpVar *var){
        if(var!=NULL){
            if(var->type==TmpVar::VAR){
                if(globalVars.count(var)==0 && localVarSet.count(var)==0){ // not initialized local var
                    return true;
                }
            }
        }
        return false;
    }


};


#endif //C0COMPILER_SIMPLEMIPSGENERATOR_H
